   /*******************************************************/
   /*      "C" Language Integrated Production System      */
   /*                                                     */
   /*             CLIPS Version 6.30  10/19/06            */
   /*                                                     */
   /*                    ENGINE MODULE                    */
   /*******************************************************/

/*************************************************************/
/* Purpose: Provides functionality primarily associated with */
/*   the run and focus commands.                             */
/*                                                           */
/* Principal Programmer(s):                                  */
/*      Gary D. Riley                                        */
/*                                                           */
/* Contributing Programmer(s):                               */
/*      Bebe Ly                                              */
/*      Brian L. Dantes                                      */
/*                                                           */
/* Revision History:                                         */
/*      6.23: Correction for FalseSymbol/TrueSymbol. DR0859  */
/*                                                           */
/*            Corrected compilation errors for files         */
/*            generated by constructs-to-c. DR0861           */
/*                                                           */
/*      6.24: Removed DYNAMIC_SALIENCE, INCREMENTAL_RESET,   */
/*            and LOGICAL_DEPENDENCIES compilation flags.    */
/*                                                           */
/*            Renamed BOOLEAN macro type to intBool.         */
/*                                                           */
/*            Added access functions to the HaltRules flag.  */
/*                                                           */
/*            Added EnvGetNextFocus, EnvGetFocusChanged, and */
/*            EnvSetFocusChanged functions.                  */
/*                                                           */
/*      6.30: Added additional developer statistics to help  */
/*            analyze join network performance.              */
/*                                                           */
/*            Removed pseudo-facts used in not CEs.          */
/*                                                           */
/*            Added context information for run functions.   */
/*                                                           */
/*************************************************************/

#define _ENGINE_SOURCE_

#include <stdio.h>
#define _STDIO_INCLUDED_
#include <string.h>

#include "setup.h"

#if DEFRULE_CONSTRUCT

#include "agenda.h"
#include "argacces.h"
#include "constant.h"
#include "envrnmnt.h"
#include "fact/fact_manager.h"
#include "inscom.h"
#include "memalloc.h"
#include "modulutl.h"
#include "prccode.h"
#include "prcdrfun.h"
#include "proflfun.h"
#include "reteutil.h"
#include "retract.h"
#include "router.h"
#include "ruledlt.h"
#include "sysdep.h"
#include "utility.h"
#include "watch.h"

#include "engine.h"

/***************************************/
/* LOCAL INTERNAL FUNCTION DEFINITIONS */
/***************************************/

   static struct activation      *NextActivationToFire(void *,EXEC_STATUS);
   static struct defmodule       *RemoveFocus(void *,EXEC_STATUS,struct defmodule *);
   static void                    DeallocateEngineData(void *,EXEC_STATUS);

/*****************************************************************************/
/* InitializeEngine: Initializes the activations and statistics watch items. */
/*****************************************************************************/
globle void InitializeEngine(
  void *theEnv,
  EXEC_STATUS)
  {   
   AllocateEnvironmentData(theEnv,execStatus,ENGINE_DATA,sizeof(struct engineData),DeallocateEngineData);

   EngineData(theEnv,execStatus)->IncrementalResetFlag = TRUE;
   
#if DEBUGGING_FUNCTIONS
   AddWatchItem(theEnv,execStatus,"statistics",0,&EngineData(theEnv,execStatus)->WatchStatistics,20,NULL,NULL);
   AddWatchItem(theEnv,execStatus,"focus",0,&EngineData(theEnv,execStatus)->WatchFocus,0,NULL,NULL);
#endif
  }
  
/*************************************************/
/* DeallocateEngineData: Deallocates environment */
/*    data for engine functionality.             */
/*************************************************/
static void DeallocateEngineData(
  void *theEnv,
  EXEC_STATUS)
  {
   struct focus *tmpPtr, *nextPtr;
   
   DeallocateCallList(theEnv,execStatus,EngineData(theEnv,execStatus)->ListOfRunFunctions);

   tmpPtr = EngineData(theEnv,execStatus)->CurrentFocus;
   while (tmpPtr != NULL)
     {
      nextPtr = tmpPtr->next;
      rtn_struct(theEnv,execStatus,focus,tmpPtr);
      tmpPtr = nextPtr;
     }
  }

/**********************************************/
/* Run: C access routine for the run command. */
/**********************************************/
#if ALLOW_ENVIRONMENT_GLOBALS
globle long long Run(
  long long runLimit)
  {
   return EnvRun(GetCurrentEnvironment(),GetCurrentExecutionStatus(),runLimit);
  }
#endif
  
/*************************************************/
/* EnvRun: C access routine for the run command. */
/*************************************************/
globle long long EnvRun(
  void *theEnv,
  EXEC_STATUS,
  long long runLimit)
  {
   long long rulesFired = 0;
   DATA_OBJECT result;
   struct callFunctionItem *theRunFunction;
#if DEBUGGING_FUNCTIONS
   unsigned long maxActivations = 0, sumActivations = 0;
#if DEFTEMPLATE_CONSTRUCT
   unsigned long maxFacts = 0, sumFacts = 0;
#endif
#if OBJECT_SYSTEM
   unsigned long maxInstances = 0, sumInstances = 0;
#endif
   double endTime, startTime = 0.0;
   unsigned long tempValue;
#endif
   unsigned short i;
   struct patternEntity *theMatchingItem;
   struct partialMatch *theBasis;
   ACTIVATION *theActivation;
   char *ruleFiring;
#if PROFILING_FUNCTIONS
   struct profileFrameInfo profileFrame;
#endif
   struct trackedMemory *theTM;

   /*=====================================================*/
   /* Make sure the run command is not already executing. */
   /*=====================================================*/

   if (EngineData(theEnv,execStatus)->AlreadyRunning) return(0);
   EngineData(theEnv,execStatus)->AlreadyRunning = TRUE;

   /*================================*/
   /* Set up statistics information. */
   /*================================*/

#if DEBUGGING_FUNCTIONS
   if (EngineData(theEnv,execStatus)->WatchStatistics)
     {
#if DEFTEMPLATE_CONSTRUCT
      maxFacts = GetNumberOfFacts(theEnv,execStatus);
      sumFacts = maxFacts;
#endif
#if OBJECT_SYSTEM
      maxInstances = GetGlobalNumberOfInstances(theEnv,execStatus);
      sumInstances = maxInstances;
#endif
      maxActivations = GetNumberOfActivations(theEnv,execStatus);
      sumActivations = maxActivations;
      startTime = gentime();
     }
#endif

   /*=============================*/
   /* Set up execution variables. */
   /*=============================*/

   if (execStatus->CurrentEvaluationDepth == 0) SetHaltExecution(theEnv,execStatus,FALSE);
   EngineData(theEnv,execStatus)->HaltRules = FALSE;

#if DEVELOPER
   EngineData(theEnv,execStatus)->leftToRightComparisons = 0;
   EngineData(theEnv,execStatus)->rightToLeftComparisons = 0;
   EngineData(theEnv,execStatus)->leftToRightSucceeds = 0;
   EngineData(theEnv,execStatus)->rightToLeftSucceeds = 0;
   EngineData(theEnv,execStatus)->leftToRightLoops = 0;
   EngineData(theEnv,execStatus)->rightToLeftLoops = 0;
   EngineData(theEnv,execStatus)->findNextConflictingComparisons = 0;
   EngineData(theEnv,execStatus)->betaHashListSkips = 0;
   EngineData(theEnv,execStatus)->betaHashHTSkips = 0;
   EngineData(theEnv,execStatus)->unneededMarkerCompare = 0;
#endif

   /*=====================================================*/
   /* Fire rules until the agenda is empty, the run limit */
   /* has been reached, or a rule execution error occurs. */
   /*=====================================================*/

   theActivation = NextActivationToFire(theEnv,execStatus);
   while ((theActivation != NULL) &&
          (runLimit != 0) &&
          (execStatus->HaltExecution == FALSE) &&
          (EngineData(theEnv,execStatus)->HaltRules == FALSE))
     {
      /*===========================================*/
      /* Detach the activation from the agenda and */
      /* determine which rule is firing.           */
      /*===========================================*/

      DetachActivation(theEnv,execStatus,theActivation);
      theTM = AddTrackedMemory(theEnv,execStatus,theActivation,sizeof(struct activation));
      ruleFiring = EnvGetActivationName(theEnv,execStatus,theActivation);
      theBasis = (struct partialMatch *) GetActivationBasis(theActivation);
      EngineData(theEnv,execStatus)->ExecutingRule = (struct defrule *) GetActivationRule(theActivation);

      /*=============================================*/
      /* Update the number of rules that have fired. */
      /*=============================================*/

      rulesFired++;
      if (runLimit > 0) { runLimit--; }

      /*==================================*/
      /* If rules are being watched, then */
      /* print an information message.    */
      /*==================================*/

#if DEBUGGING_FUNCTIONS
      if (EngineData(theEnv,execStatus)->ExecutingRule->watchFiring)
        {
         char printSpace[60];

         gensprintf(printSpace,"FIRE %4lld ",rulesFired);
         EnvPrintRouter(theEnv,execStatus,WTRACE,printSpace);
         EnvPrintRouter(theEnv,execStatus,WTRACE,ruleFiring);
         EnvPrintRouter(theEnv,execStatus,WTRACE,": ");
         PrintPartialMatch(theEnv,execStatus,WTRACE,theBasis);
         EnvPrintRouter(theEnv,execStatus,WTRACE,"\n");
        }
#endif

      /*=================================================*/
      /* Remove the link between the activation and the  */
      /* completed match for the rule. Set the busy flag */
      /* for the completed match to TRUE (so the match   */
      /* upon which our RHS variables are dependent is   */
      /* not deleted while our rule is firing). Set up   */
      /* the global pointers to the completed match for  */
      /* routines which do variable extractions.         */
      /*=================================================*/

      theBasis->marker = NULL;
      theBasis->busy = TRUE;

      LocalEngineData(theEnv,execStatus).LHSBinds = theBasis;
      LocalEngineData(theEnv,execStatus).RHSBinds = NULL;

      /*===================================================================*/
      /* Increment the count for each of the facts/objects associated with */
      /* the rule activation so that the facts/objects cannot be deleted   */
      /* by garbage collection while the rule is executing.                */
      /*===================================================================*/

      for (i = 0; i < theBasis->bcount; i++)
        {
         if (theBasis->binds[i].gm.theMatch == NULL) continue;
         theMatchingItem = theBasis->binds[i].gm.theMatch->matchingItem;
         if (theMatchingItem != NULL)
           { (*theMatchingItem->theInfo->incrementBasisCount)(theEnv,execStatus,theMatchingItem); }
        }

      /*====================================================*/
      /* Execute the rule's right hand side actions. If the */
      /* rule has logical CEs, set up the pointer to the    */
      /* rules logical join so the assert command will      */
      /* attach the appropriate dependencies to the facts.  */
      /*====================================================*/

      EngineData(theEnv,execStatus)->TheLogicalJoin = EngineData(theEnv,execStatus)->ExecutingRule->logicalJoin;
      
      if (EngineData(theEnv,execStatus)->TheLogicalJoin != NULL)
        { 
         EngineData(theEnv,execStatus)->TheLogicalBind = FindLogicalBind(EngineData(theEnv,execStatus)->TheLogicalJoin,LocalEngineData(theEnv,execStatus).LHSBinds); 
         EngineData(theEnv,execStatus)->TheLogicalBind->busy = TRUE; 
        }
      else
        { EngineData(theEnv,execStatus)->TheLogicalBind = NULL; }

      execStatus->CurrentEvaluationDepth++;
      SetEvaluationError(theEnv,execStatus,FALSE);
      EngineData(theEnv,execStatus)->ExecutingRule->executing = TRUE;

#if PROFILING_FUNCTIONS
      StartProfile(theEnv,execStatus,&profileFrame,
                   &EngineData(theEnv,execStatus)->ExecutingRule->header.usrData,
                   ProfileFunctionData(theEnv,execStatus)->ProfileConstructs);
#endif

      EvaluateProcActions(theEnv,execStatus,EngineData(theEnv,execStatus)->ExecutingRule->header.whichModule->theModule,
                          EngineData(theEnv,execStatus)->ExecutingRule->actions,EngineData(theEnv,execStatus)->ExecutingRule->localVarCnt,
                          &result,NULL);

#if PROFILING_FUNCTIONS
      EndProfile(theEnv,execStatus,&profileFrame);
#endif

      EngineData(theEnv,execStatus)->ExecutingRule->executing = FALSE;
      SetEvaluationError(theEnv,execStatus,FALSE);
      execStatus->CurrentEvaluationDepth--;
      EngineData(theEnv,execStatus)->TheLogicalJoin = NULL;
      
      if (EngineData(theEnv,execStatus)->TheLogicalBind != NULL)
        {
         EngineData(theEnv,execStatus)->TheLogicalBind->busy = FALSE;
         EngineData(theEnv,execStatus)->TheLogicalBind = NULL;
        }

      /*=====================================================*/
      /* If rule execution was halted, then print a message. */
      /*=====================================================*/

#if DEBUGGING_FUNCTIONS
      if ((execStatus->HaltExecution) || (EngineData(theEnv,execStatus)->HaltRules && EngineData(theEnv,execStatus)->ExecutingRule->watchFiring))
#else
      if ((execStatus->HaltExecution) || (EngineData(theEnv,execStatus)->HaltRules))
#endif

        {
         PrintErrorID(theEnv,execStatus,"PRCCODE",4,FALSE);
         EnvPrintRouter(theEnv,execStatus,WERROR,"Execution halted during the actions of defrule ");
         EnvPrintRouter(theEnv,execStatus,WERROR,ruleFiring);
         EnvPrintRouter(theEnv,execStatus,WERROR,".\n");
        }

      /*===================================================*/
      /* Decrement the count for each of the facts/objects */
      /* associated with the rule activation.              */
      /*===================================================*/

      theBasis->busy = FALSE;

      for (i = 0; i < (theBasis->bcount); i++)
        {
         if (theBasis->binds[i].gm.theMatch == NULL) continue;
         theMatchingItem = theBasis->binds[i].gm.theMatch->matchingItem;
         if (theMatchingItem != NULL)
           { (*theMatchingItem->theInfo->decrementBasisCount)(theEnv,execStatus,theMatchingItem); }
        }

      /*========================================*/
      /* Return the agenda node to free memory. */
      /*========================================*/

      RemoveTrackedMemory(theEnv,execStatus,theTM);
      RemoveActivation(theEnv,execStatus,theActivation,FALSE,FALSE);

      /*======================================*/
      /* Get rid of partial matches discarded */
      /* while executing the rule's RHS.      */
      /*======================================*/

      FlushGarbagePartialMatches(theEnv,execStatus);

      /*==================================*/
      /* Get rid of other garbage created */
      /* while executing the rule's RHS.  */
      /*==================================*/

      PeriodicCleanup(theEnv,execStatus,FALSE,TRUE);

      /*==========================*/
      /* Keep up with statistics. */
      /*==========================*/

#if DEBUGGING_FUNCTIONS
      if (EngineData(theEnv,execStatus)->WatchStatistics)
        {
#if DEFTEMPLATE_CONSTRUCT
         tempValue = GetNumberOfFacts(theEnv,execStatus);
         if (tempValue > maxFacts) maxFacts = tempValue;
         sumFacts += tempValue;
#endif
#if OBJECT_SYSTEM
         tempValue = GetGlobalNumberOfInstances(theEnv,execStatus);
         if (tempValue > maxInstances) maxInstances = tempValue;
         sumInstances += tempValue;
#endif
         tempValue = GetNumberOfActivations(theEnv,execStatus);
         if (tempValue > maxActivations) maxActivations = tempValue;
         sumActivations += tempValue;
        }
#endif

      /*==================================*/
      /* Update saliences if appropriate. */
      /*==================================*/

      if (EnvGetSalienceEvaluation(theEnv,execStatus) == EVERY_CYCLE) EnvRefreshAgenda(theEnv,execStatus,NULL);

      /*========================================*/
      /* Execute the list of functions that are */
      /* to be called after each rule firing.   */
      /*========================================*/

      for (theRunFunction = EngineData(theEnv,execStatus)->ListOfRunFunctions;
           theRunFunction != NULL;
           theRunFunction = theRunFunction->next)
        { 
         SetEnvironmentCallbackContext(theEnv,execStatus,theRunFunction->context);
         if (theRunFunction->environmentAware)
           { (*theRunFunction->func)(theEnv,execStatus); }
         else            
           { ((void (*)(void))(*theRunFunction->func))(); }
        }

      /*========================================*/
      /* If a return was issued on the RHS of a */
      /* rule, then remove *that* rule's module */
      /* from the focus stack                   */
      /*========================================*/

      if (ProcedureFunctionData(theEnv,execStatus)->ReturnFlag == TRUE)
        { RemoveFocus(theEnv,execStatus,EngineData(theEnv,execStatus)->ExecutingRule->header.whichModule->theModule); }
      ProcedureFunctionData(theEnv,execStatus)->ReturnFlag = FALSE;

      /*========================================*/
      /* Determine the next activation to fire. */
      /*========================================*/

      theActivation = (struct activation *) NextActivationToFire(theEnv,execStatus);

      /*==============================*/
      /* Check for a rule breakpoint. */
      /*==============================*/

      if (theActivation != NULL)
        {
         if (((struct defrule *) GetActivationRule(theActivation))->afterBreakpoint)
           {
            EngineData(theEnv,execStatus)->HaltRules = TRUE;
            EnvPrintRouter(theEnv,execStatus,WDIALOG,"Breaking on rule ");
            EnvPrintRouter(theEnv,execStatus,WDIALOG,EnvGetActivationName(theEnv,execStatus,theActivation));
            EnvPrintRouter(theEnv,execStatus,WDIALOG,".\n");
           }
        }
     }

   /*=====================================================*/
   /* Make sure run functions are executed at least once. */
   /*=====================================================*/

   if (rulesFired == 0)
     {
      for (theRunFunction = EngineData(theEnv,execStatus)->ListOfRunFunctions;
           theRunFunction != NULL;
           theRunFunction = theRunFunction->next)
        { 
         if (theRunFunction->environmentAware)
           { (*theRunFunction->func)(theEnv,execStatus); }
         else            
           { ((void (*)(void))(*theRunFunction->func))(); }
        }
     }

   /*======================================================*/
   /* If rule execution was halted because the rule firing */
   /* limit was reached, then print a message.             */
   /*======================================================*/

   if (runLimit == rulesFired)
     { EnvPrintRouter(theEnv,execStatus,WDIALOG,"rule firing limit reached\n"); }

   /*==============================*/
   /* Restore execution variables. */
   /*==============================*/

   EngineData(theEnv,execStatus)->ExecutingRule = NULL;
   EngineData(theEnv,execStatus)->HaltRules = FALSE;

   /*=================================================*/
   /* Print out statistics if they are being watched. */
   /*=================================================*/

#if DEBUGGING_FUNCTIONS
   if (EngineData(theEnv,execStatus)->WatchStatistics)
     {
      char printSpace[60];

      endTime = gentime();

      PrintLongInteger(theEnv,execStatus,WDIALOG,rulesFired);
      EnvPrintRouter(theEnv,execStatus,WDIALOG," rules fired");

#if (! GENERIC)
      if (startTime != endTime)
        {
         EnvPrintRouter(theEnv,execStatus,WDIALOG,"        Run time is ");
         PrintFloat(theEnv,execStatus,WDIALOG,endTime - startTime);
         EnvPrintRouter(theEnv,execStatus,WDIALOG," seconds.\n");
         PrintFloat(theEnv,execStatus,WDIALOG,(double) rulesFired / (endTime - startTime));
         EnvPrintRouter(theEnv,execStatus,WDIALOG," rules per second.\n");
        }
      else
        { EnvPrintRouter(theEnv,execStatus,WDIALOG,"\n"); }
#endif

#if DEFTEMPLATE_CONSTRUCT
      gensprintf(printSpace,"%ld mean number of facts (%ld maximum).\n",
                          (long) (((double) sumFacts / (rulesFired + 1)) + 0.5),
                          maxFacts);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);
#endif

#if OBJECT_SYSTEM
      gensprintf(printSpace,"%ld mean number of instances (%ld maximum).\n",
                          (long) (((double) sumInstances / (rulesFired + 1)) + 0.5),
                          maxInstances);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);
#endif

      gensprintf(printSpace,"%ld mean number of activations (%ld maximum).\n",
                          (long) (((double) sumActivations / (rulesFired + 1)) + 0.5),
                          maxActivations);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);
      
#if DEVELOPER
      gensprintf(printSpace,"%9ld left to right comparisons.\n",
                          EngineData(theEnv,execStatus)->leftToRightComparisons);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld left to right succeeds.\n",
                          EngineData(theEnv,execStatus)->leftToRightSucceeds);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld left to right loops.\n",
                          EngineData(theEnv,execStatus)->leftToRightLoops);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld right to left comparisons.\n",
                          EngineData(theEnv,execStatus)->rightToLeftComparisons);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld right to left succeeds.\n",
                          EngineData(theEnv,execStatus)->rightToLeftSucceeds);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld right to left loops.\n",
                          EngineData(theEnv,execStatus)->rightToLeftLoops);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld find next conflicting comparisons.\n",
                          EngineData(theEnv,execStatus)->findNextConflictingComparisons);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld beta hash list skips.\n",
                          EngineData(theEnv,execStatus)->betaHashListSkips);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);
      
      gensprintf(printSpace,"%9ld beta hash hash table skips.\n",
                          EngineData(theEnv,execStatus)->betaHashHTSkips);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

      gensprintf(printSpace,"%9ld unneeded marker compare.\n",
                          EngineData(theEnv,execStatus)->unneededMarkerCompare);
      EnvPrintRouter(theEnv,execStatus,WDIALOG,printSpace);

#endif
     }
#endif

   /*==========================================*/
   /* The current module should be the current */
   /* focus when the run finishes.             */
   /*==========================================*/

   if (EngineData(theEnv,execStatus)->CurrentFocus != NULL)
     {
      if (EngineData(theEnv,execStatus)->CurrentFocus->theModule != ((struct defmodule *) EnvGetCurrentModule(theEnv,execStatus)))
        { EnvSetCurrentModule(theEnv,execStatus,(void *) EngineData(theEnv,execStatus)->CurrentFocus->theModule); }
     }

   /*===================================*/
   /* Return the number of rules fired. */
   /*===================================*/

   EngineData(theEnv,execStatus)->AlreadyRunning = FALSE;
   return(rulesFired);
  }

/***********************************************************/
/* NextActivationToFire: Returns the next activation which */
/*   should be executed based on the current focus.        */
/***********************************************************/
static struct activation *NextActivationToFire(
  void *theEnv,
  EXEC_STATUS)
  {
   struct activation *theActivation;
   struct defmodule *theModule;

   /*====================================*/
   /* If there is no current focus, then */
   /* focus on the MAIN module.          */
   /*====================================*/

   if (EngineData(theEnv,execStatus)->CurrentFocus == NULL)
     {
      theModule = (struct defmodule *) EnvFindDefmodule(theEnv,execStatus,"MAIN");
      EnvFocus(theEnv,execStatus,theModule);
     }

   /*===========================================================*/
   /* Determine the top activation on the agenda of the current */
   /* focus. If the current focus has no activations on its     */
   /* agenda, then pop the focus off the focus stack until      */
   /* a focus that has an activation on its agenda is found.    */
   /*===========================================================*/

   theActivation = EngineData(theEnv,execStatus)->CurrentFocus->theDefruleModule->agenda;
   while ((theActivation == NULL) && (EngineData(theEnv,execStatus)->CurrentFocus != NULL))
     {
      if (EngineData(theEnv,execStatus)->CurrentFocus != NULL) EnvPopFocus(theEnv,execStatus);
      if (EngineData(theEnv,execStatus)->CurrentFocus != NULL) theActivation = EngineData(theEnv,execStatus)->CurrentFocus->theDefruleModule->agenda;
     }

   /*=========================================*/
   /* Return the next activation to be fired. */
   /*=========================================*/

   return(theActivation);
  }

/***************************************************/
/* RemoveFocus: Removes the first occurence of the */
/*   specified module from the focus stack.        */
/***************************************************/
static struct defmodule *RemoveFocus(
  void *theEnv,
  EXEC_STATUS,
  struct defmodule *theModule)
  {
   struct focus *tempFocus,*prevFocus, *nextFocus;
   int found = FALSE;
   int currentFocusRemoved = FALSE;

   /*====================================*/
   /* Return NULL if there is nothing on */
   /* the focus stack to remove.         */
   /*====================================*/

   if (EngineData(theEnv,execStatus)->CurrentFocus == NULL) return(NULL);

   /*=============================================*/
   /* Remove the first occurence of the specified */
   /* module from the focus stack.                */
   /*=============================================*/

   prevFocus = NULL;
   tempFocus = EngineData(theEnv,execStatus)->CurrentFocus;
   while ((tempFocus != NULL) && (! found))
     {
      if (tempFocus->theModule == theModule)
        {
         found = TRUE;

         nextFocus = tempFocus->next;
         rtn_struct(theEnv,execStatus,focus,tempFocus);
         tempFocus = nextFocus;

         if (prevFocus == NULL)
           {
            currentFocusRemoved = TRUE;
            EngineData(theEnv,execStatus)->CurrentFocus = tempFocus;
           }
         else
           { prevFocus->next = tempFocus; }
        }
      else
        {
         prevFocus = tempFocus;
         tempFocus = tempFocus->next;
        }
     }

   /*=========================================*/
   /* If the given module is not in the focus */
   /* stack, simply return the current focus  */
   /*=========================================*/

   if (! found) return(EngineData(theEnv,execStatus)->CurrentFocus->theModule);

   /*========================================*/
   /* If the current focus is being watched, */
   /* then print an informational message.   */
   /*========================================*/

#if DEBUGGING_FUNCTIONS
   if (EngineData(theEnv,execStatus)->WatchFocus)
     {
      EnvPrintRouter(theEnv,execStatus,WTRACE,"<== Focus ");
      EnvPrintRouter(theEnv,execStatus,WTRACE,ValueToString(theModule->name));

      if ((EngineData(theEnv,execStatus)->CurrentFocus != NULL) && currentFocusRemoved)
        {
         EnvPrintRouter(theEnv,execStatus,WTRACE," to ");
         EnvPrintRouter(theEnv,execStatus,WTRACE,ValueToString(EngineData(theEnv,execStatus)->CurrentFocus->theModule->name));
        }

      EnvPrintRouter(theEnv,execStatus,WTRACE,"\n");
     }
#endif

   /*======================================================*/
   /* Set the current module to the module associated with */
   /* the current focus (if it changed) and set a boolean  */
   /* flag indicating that the focus has changed.          */
   /*======================================================*/

   if ((EngineData(theEnv,execStatus)->CurrentFocus != NULL) && currentFocusRemoved)
     { EnvSetCurrentModule(theEnv,execStatus,(void *) EngineData(theEnv,execStatus)->CurrentFocus->theModule); }
   EngineData(theEnv,execStatus)->FocusChanged = TRUE;

   /*====================================*/
   /* Return the module that was removed */
   /* from the focus stack.              */
   /*====================================*/

   return(theModule);
  }

/*************************************************************/
/* EnvPopFocus: C access routine for the pop-focus function. */
/*************************************************************/
globle void *EnvPopFocus(
  void *theEnv,
  EXEC_STATUS)
  {
   if (EngineData(theEnv,execStatus)->CurrentFocus == NULL) return(NULL);
   return((void *) RemoveFocus(theEnv,execStatus,EngineData(theEnv,execStatus)->CurrentFocus->theModule));
  }

/***************************************************************/
/* EnvGetNextFocus: Returns the next focus on the focus stack. */
/***************************************************************/
globle void *EnvGetNextFocus(
  void *theEnv,
  EXEC_STATUS,
  void *theFocus)
  {
   /*==================================================*/
   /* If NULL is passed as an argument, return the top */
   /* focus on the focus stack (the current focus).    */
   /*==================================================*/

   if (theFocus == NULL) return((void *) EngineData(theEnv,execStatus)->CurrentFocus);

   /*=======================================*/
   /* Otherwise, return the focus following */
   /* the focus passed as an argument.      */
   /*=======================================*/

   return((void *) ((struct focus *) theFocus)->next);
  }

/******************************************************/
/* EnvFocus: C access routine for the focus function. */
/******************************************************/
globle void EnvFocus(
  void *theEnv,
  EXEC_STATUS,
  void *vTheModule)
  {
   struct defmodule *theModule = (struct defmodule *) vTheModule;
   struct focus *tempFocus;

   /*==================================================*/
   /* Make the specified module be the current module. */
   /* If the specified module is the current focus,    */
   /* then no further action is needed.                */
   /*==================================================*/

   EnvSetCurrentModule(theEnv,execStatus,(void *) theModule);
   if (EngineData(theEnv,execStatus)->CurrentFocus != NULL)
     { if (EngineData(theEnv,execStatus)->CurrentFocus->theModule == theModule) return; }

   /*=====================================*/
   /* If the focus is being watched, then */
   /* print an information message.       */
   /*=====================================*/

#if DEBUGGING_FUNCTIONS
   if (EngineData(theEnv,execStatus)->WatchFocus)
     {
      EnvPrintRouter(theEnv,execStatus,WTRACE,"==> Focus ");
      EnvPrintRouter(theEnv,execStatus,WTRACE,ValueToString(theModule->name));
      if (EngineData(theEnv,execStatus)->CurrentFocus != NULL)
        {
         EnvPrintRouter(theEnv,execStatus,WTRACE," from ");
         EnvPrintRouter(theEnv,execStatus,WTRACE,ValueToString(EngineData(theEnv,execStatus)->CurrentFocus->theModule->name));
        }
      EnvPrintRouter(theEnv,execStatus,WTRACE,"\n");
     }
#endif

   /*=======================================*/
   /* Add the new focus to the focus stack. */
   /*=======================================*/

   tempFocus = get_struct(theEnv,execStatus,focus);
   tempFocus->theModule = theModule;
   tempFocus->theDefruleModule = GetDefruleModuleItem(theEnv,execStatus,theModule);
   tempFocus->next = EngineData(theEnv,execStatus)->CurrentFocus;
   EngineData(theEnv,execStatus)->CurrentFocus = tempFocus;
   EngineData(theEnv,execStatus)->FocusChanged = TRUE;
  }

/************************************************/
/* ClearFocusStackCommand: H/L access routine   */
/*   for the clear-focus-stack command.         */
/************************************************/
globle void ClearFocusStackCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   if (EnvArgCountCheck(theEnv,execStatus,"list-focus-stack",EXACTLY,0) == -1) return;

   EnvClearFocusStack(theEnv,execStatus);
  }

/****************************************/
/* EnvClearFocusStack: C access routine */
/*   for the clear-focus-stack command. */
/****************************************/
globle void EnvClearFocusStack(
  void *theEnv,
  EXEC_STATUS)
  {
   while (EngineData(theEnv,execStatus)->CurrentFocus != NULL) EnvPopFocus(theEnv,execStatus);

   EngineData(theEnv,execStatus)->FocusChanged = TRUE;
  }

#if ALLOW_ENVIRONMENT_GLOBALS
/***********************************/
/* AddRunFunction: Adds a function */
/*   to the ListOfRunFunctions.    */
/***********************************/
globle intBool AddRunFunction(
  char *name,
  void (*functionPtr)(void),
  int priority)
  {
   void *theEnv = GetCurrentEnvironment();
   EXEC_STATUS = GetCurrentExecutionStatus();

   EngineData(theEnv,execStatus)->ListOfRunFunctions = 
       AddFunctionToCallList(theEnv,execStatus,name,priority,(void (*)(void *,EXEC_STATUS)) functionPtr,
                             EngineData(theEnv,execStatus)->ListOfRunFunctions,TRUE);
   return(1);
  }
#endif

/**************************************/
/* EnvAddRunFunction: Adds a function */
/*   to the ListOfRunFunctions.       */
/**************************************/
globle intBool EnvAddRunFunction(
  void *theEnv,
  EXEC_STATUS,
  char *name,
  void (*functionPtr)(void *,EXEC_STATUS),
  int priority)
  {
   EngineData(theEnv,execStatus)->ListOfRunFunctions = AddFunctionToCallList(theEnv,execStatus,name,priority,
                                              functionPtr,
                                              EngineData(theEnv,execStatus)->ListOfRunFunctions,TRUE);
   return(1);
  }
  
/*****************************************/
/* EnvAddRunFunctionWithContext: Adds a  */
/*   function to the ListOfRunFunctions. */
/*****************************************/
globle intBool EnvAddRunFunctionWithContext(
  void *theEnv,
  EXEC_STATUS,
  char *name,
  void (*functionPtr)(void *,EXEC_STATUS),
  int priority,
  void *context)
  {
   EngineData(theEnv,execStatus)->ListOfRunFunctions = 
      AddFunctionToCallListWithContext(theEnv,execStatus,name,priority,functionPtr,
                                       EngineData(theEnv,execStatus)->ListOfRunFunctions,
                                       TRUE,context);
   return(1);
  }
  
/********************************************/
/* EnvRemoveRunFunction: Removes a function */
/*   from the ListOfRunFunctions.           */
/********************************************/
globle intBool EnvRemoveRunFunction(
  void *theEnv,
  EXEC_STATUS,
  char *name)
  {
   int found;

   EngineData(theEnv,execStatus)->ListOfRunFunctions = 
      RemoveFunctionFromCallList(theEnv,execStatus,name,EngineData(theEnv,execStatus)->ListOfRunFunctions,&found);

   if (found) return(TRUE);

   return(FALSE);
  }

/*********************************************************/
/* RunCommand: H/L access routine for the run command.   */
/*********************************************************/
globle void RunCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   int numArgs;
   long long runLimit = -1LL;
   DATA_OBJECT argPtr;

   if ((numArgs = EnvArgCountCheck(theEnv,execStatus,"run",NO_MORE_THAN,1)) == -1) return;

   if (numArgs == 0)
     { runLimit = -1LL; }
   else if (numArgs == 1)
     {
      if (EnvArgTypeCheck(theEnv,execStatus,"run",1,INTEGER,&argPtr) == FALSE) return;
      runLimit = DOToLong(argPtr);
     }

   EnvRun(theEnv,execStatus,runLimit);

   return;
  }

/***********************************************/
/* HaltCommand: Causes rule execution to halt. */
/***********************************************/
globle void HaltCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   EnvArgCountCheck(theEnv,execStatus,"halt",EXACTLY,0);
   EnvHalt(theEnv,execStatus);
  }

/*****************************/
/* EnvHalt: C access routine */
/*   for the halt command.   */
/*****************************/
globle void EnvHalt(
  void *theEnv,
  EXEC_STATUS)
  {
   EngineData(theEnv,execStatus)->HaltRules = TRUE;
  }

#if DEBUGGING_FUNCTIONS

/*********************************/
/* EnvSetBreak: C access routine */
/*   for the set-break command.  */
/*********************************/
#if WIN_BTC
#pragma argsused
#endif
globle void EnvSetBreak(
  void *theEnv,
  EXEC_STATUS,
  void *theRule)
  {
#if MAC_MCW || WIN_MCW || MAC_XCD
#pragma unused(theEnv,execStatus)
#endif
   struct defrule *thePtr;

   for (thePtr = (struct defrule *) theRule;
        thePtr != NULL;
        thePtr = thePtr->disjunct)
     { thePtr->afterBreakpoint = 1; }
  }

/************************************/
/* EnvRemoveBreak: C access routine */
/*   for the remove-break command.  */
/************************************/
#if WIN_BTC
#pragma argsused
#endif
globle intBool EnvRemoveBreak(
  void *theEnv,
  EXEC_STATUS,
  void *theRule)
  {
#if MAC_MCW || WIN_MCW || MAC_XCD
#pragma unused(theEnv,execStatus)
#endif
   struct defrule *thePtr;
   int rv = FALSE;

   for (thePtr = (struct defrule *) theRule;
        thePtr != NULL;
        thePtr = thePtr->disjunct)
     {
      if (thePtr->afterBreakpoint == 1)
        {
         thePtr->afterBreakpoint = 0;
         rv = TRUE;
        }
     }

   return(rv);
  }

/**************************************************/
/* RemoveAllBreakpoints: Removes all breakpoints. */
/**************************************************/
globle void RemoveAllBreakpoints(
  void *theEnv,
  EXEC_STATUS)
  {
   void *theRule;
   void *theDefmodule = NULL;

   while ((theDefmodule = EnvGetNextDefmodule(theEnv,execStatus,theDefmodule)) != NULL)
     {
      theRule = NULL;
      while ((theRule = EnvGetNextDefrule(theEnv,execStatus,theRule)) != NULL)
        { EnvRemoveBreak(theEnv,execStatus,theRule); }
     }
  }

/***********************************/
/* EnvShowBreaks: C access routine */
/*   for the show-breaks command.  */
/***********************************/
globle void EnvShowBreaks(
  void *theEnv,
  EXEC_STATUS,
  char *logicalName,
  void *vTheModule)
  {
   ListItemsDriver(theEnv,execStatus,logicalName,(struct defmodule *) vTheModule,
                   NULL,NULL,
                   EnvGetNextDefrule,(char *(*)(void *,EXEC_STATUS)) GetConstructNameString,
                   NULL,EnvDefruleHasBreakpoint);
   }

/**********************************************/
/* EnvDefruleHasBreakpoint: Indicates whether */
/*   the specified rule has a breakpoint set. */
/**********************************************/
#if WIN_BTC
#pragma argsused
#endif
globle intBool EnvDefruleHasBreakpoint(
  void *theEnv,
  EXEC_STATUS,
  void *theRule)
  {
#if MAC_MCW || WIN_MCW || MAC_XCD
#pragma unused(theEnv,execStatus)
#endif

   return(((struct defrule *) theRule)->afterBreakpoint);
  }

/*****************************************/
/* SetBreakCommand: H/L access routine   */
/*   for the set-break command.          */
/*****************************************/
globle void SetBreakCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   DATA_OBJECT argPtr;
   char *argument;
   void *defrulePtr;

   if (EnvArgCountCheck(theEnv,execStatus,"set-break",EXACTLY,1) == -1) return;

   if (EnvArgTypeCheck(theEnv,execStatus,"set-break",1,SYMBOL,&argPtr) == FALSE) return;

   argument = DOToString(argPtr);

   if ((defrulePtr = EnvFindDefrule(theEnv,execStatus,argument)) == NULL)
     {
      CantFindItemErrorMessage(theEnv,execStatus,"defrule",argument);
      return;
     }

   EnvSetBreak(theEnv,execStatus,defrulePtr);
  }

/********************************************/
/* RemoveBreakCommand: H/L access routine   */
/*   for the remove-break command.          */
/********************************************/
globle void RemoveBreakCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   DATA_OBJECT argPtr;
   char *argument;
   int nargs;
   void *defrulePtr;

   if ((nargs = EnvArgCountCheck(theEnv,execStatus,"remove-break",NO_MORE_THAN,1)) == -1)
     { return; }

   if (nargs == 0)
     {
      RemoveAllBreakpoints(theEnv,execStatus);
      return;
     }

   if (EnvArgTypeCheck(theEnv,execStatus,"remove-break",1,SYMBOL,&argPtr) == FALSE) return;

   argument = DOToString(argPtr);

   if ((defrulePtr = EnvFindDefrule(theEnv,execStatus,argument)) == NULL)
     {
      CantFindItemErrorMessage(theEnv,execStatus,"defrule",argument);
      return;
     }

   if (EnvRemoveBreak(theEnv,execStatus,defrulePtr) == FALSE)
     {
      EnvPrintRouter(theEnv,execStatus,WERROR,"Rule ");
      EnvPrintRouter(theEnv,execStatus,WERROR,argument);
      EnvPrintRouter(theEnv,execStatus,WERROR," does not have a breakpoint set.\n");
     }
  }

/*******************************************/
/* ShowBreaksCommand: H/L access routine   */
/*   for the show-breaks command.          */
/*******************************************/
globle void ShowBreaksCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   int numArgs, error;
   struct defmodule *theModule;

   if ((numArgs = EnvArgCountCheck(theEnv,execStatus,"show-breaks",NO_MORE_THAN,1)) == -1) return;

   if (numArgs == 1)
     {
      theModule = GetModuleName(theEnv,execStatus,"show-breaks",1,&error);
      if (error) return;
     }
   else
     { theModule = ((struct defmodule *) EnvGetCurrentModule(theEnv,execStatus)); }

   EnvShowBreaks(theEnv,execStatus,WDISPLAY,theModule);
  }

/***********************************************/
/* ListFocusStackCommand: H/L access routine   */
/*   for the list-focus-stack command.         */
/***********************************************/
globle void ListFocusStackCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   if (EnvArgCountCheck(theEnv,execStatus,"list-focus-stack",EXACTLY,0) == -1) return;

   EnvListFocusStack(theEnv,execStatus,WDISPLAY);
  }

/***************************************/
/* EnvListFocusStack: C access routine */
/*   for the list-focus-stack command. */
/***************************************/
globle void EnvListFocusStack(
  void *theEnv,
  EXEC_STATUS,
  char *logicalName)
  {
   struct focus *theFocus;

   for (theFocus = EngineData(theEnv,execStatus)->CurrentFocus;
        theFocus != NULL;
        theFocus = theFocus->next)
     {
      EnvPrintRouter(theEnv,execStatus,logicalName,EnvGetDefmoduleName(theEnv,execStatus,theFocus->theModule));
      EnvPrintRouter(theEnv,execStatus,logicalName,"\n");
     }
  }

#endif

/***********************************************/
/* GetFocusStackFunction: H/L access routine   */
/*   for the get-focus-stack function.         */
/***********************************************/
globle void GetFocusStackFunction(
  void *theEnv,
  EXEC_STATUS,
  DATA_OBJECT_PTR returnValue)
  {
   if (EnvArgCountCheck(theEnv,execStatus,"get-focus-stack",EXACTLY,0) == -1) return;

   EnvGetFocusStack(theEnv,execStatus,returnValue);
  }

/***************************************/
/* EnvGetFocusStack: C access routine  */
/*   for the get-focus-stack function. */
/***************************************/
globle void EnvGetFocusStack(
  void *theEnv,
  EXEC_STATUS,
  DATA_OBJECT_PTR returnValue)
  {
   struct focus *theFocus;
   struct multifield *theList;
   unsigned long count = 0;

   /*===========================================*/
   /* If there is no current focus, then return */
   /* a multifield value of length zero.        */
   /*===========================================*/

   if (EngineData(theEnv,execStatus)->CurrentFocus == NULL)
     {
      SetpType(returnValue,MULTIFIELD);
      SetpDOBegin(returnValue,1);
      SetpDOEnd(returnValue,0);
      SetpValue(returnValue,(void *) EnvCreateMultifield(theEnv,execStatus,0L));
      return;
     }

   /*=====================================================*/
   /* Determine the number of modules on the focus stack. */
   /*=====================================================*/

   for (theFocus = EngineData(theEnv,execStatus)->CurrentFocus; theFocus != NULL; theFocus = theFocus->next)
     { count++; }

   /*=============================================*/
   /* Create a multifield of the appropriate size */
   /* in which to store the module names.         */
   /*=============================================*/

   SetpType(returnValue,MULTIFIELD);
   SetpDOBegin(returnValue,1);
   SetpDOEnd(returnValue,(long) count);
   theList = (struct multifield *) EnvCreateMultifield(theEnv,execStatus,count);
   SetpValue(returnValue,(void *) theList);

   /*=================================================*/
   /* Store the module names in the multifield value. */
   /*=================================================*/

   for (theFocus = EngineData(theEnv,execStatus)->CurrentFocus, count = 1;
        theFocus != NULL;
        theFocus = theFocus->next, count++)
     {
      SetMFType(theList,count,SYMBOL);
      SetMFValue(theList,count,theFocus->theModule->name);
     }
  }

/******************************************/
/* PopFocusFunction: H/L access routine   */
/*   for the pop-focus function.          */
/******************************************/
globle void *PopFocusFunction(
  void *theEnv,
  EXEC_STATUS)
  {
   struct defmodule *theModule;

   EnvArgCountCheck(theEnv,execStatus,"pop-focus",EXACTLY,0);

   theModule = (struct defmodule *) EnvPopFocus(theEnv,execStatus);
   if (theModule == NULL) return((SYMBOL_HN *) EnvFalseSymbol(theEnv,execStatus));
   return(theModule->name);
  }

/******************************************/
/* GetFocusFunction: H/L access routine   */
/*   for the get-focus function.          */
/******************************************/
globle void *GetFocusFunction(
  void *theEnv,
  EXEC_STATUS)
  {
   struct defmodule *rv;

   EnvArgCountCheck(theEnv,execStatus,"get-focus",EXACTLY,0);
   rv = (struct defmodule *) EnvGetFocus(theEnv,execStatus);
   if (rv == NULL) return((SYMBOL_HN *) EnvFalseSymbol(theEnv,execStatus));
   return(rv->name);
  }

/*********************************/
/* EnvGetFocus: C access routine */
/*   for the get-focus function. */
/*********************************/
globle void *EnvGetFocus(
  void *theEnv,
  EXEC_STATUS)
  {
   if (EngineData(theEnv,execStatus)->CurrentFocus == NULL) return(NULL);

   return((void *) EngineData(theEnv,execStatus)->CurrentFocus->theModule);
  }

/**************************************/
/* FocusCommand: H/L access routine   */
/*   for the focus function.          */
/**************************************/
globle int FocusCommand(
  void *theEnv,
  EXEC_STATUS)
  {
   DATA_OBJECT argPtr;
   char *argument;
   struct defmodule *theModule;
   int argCount, i;

   /*=====================================================*/
   /* Check for the correct number and type of arguments. */
   /*=====================================================*/

   if ((argCount = EnvArgCountCheck(theEnv,execStatus,"focus",AT_LEAST,1)) == -1)
     { return(FALSE); }

   /*===========================================*/
   /* Focus on the specified defrule module(s). */
   /*===========================================*/

   for (i = argCount; i > 0; i--)
     {
      if (EnvArgTypeCheck(theEnv,execStatus,"focus",i,SYMBOL,&argPtr) == FALSE)
        { return(FALSE); }

      argument = DOToString(argPtr);
      theModule = (struct defmodule *) EnvFindDefmodule(theEnv,execStatus,argument);

      if (theModule == NULL)
        {
         CantFindItemErrorMessage(theEnv,execStatus,"defmodule",argument);
         return(FALSE);
        }

      EnvFocus(theEnv,execStatus,(void *) theModule);
     }

   /*===================================================*/
   /* Return TRUE to indicate success of focus command. */
   /*===================================================*/

   return(TRUE);
  }

/***********************************************************************/
/* EnvGetFocusChanged: Returns the value of the variable FocusChanged. */
/***********************************************************************/
globle int EnvGetFocusChanged(
  void *theEnv,
  EXEC_STATUS)
  {
   return(EngineData(theEnv,execStatus)->FocusChanged);
  }

/********************************************************************/
/* EnvSetFocusChanged: Sets the value of the variable FocusChanged. */
/********************************************************************/
globle void EnvSetFocusChanged(
  void *theEnv,
  EXEC_STATUS,
  int value)
  {
   EngineData(theEnv,execStatus)->FocusChanged = value;
  }

/*********************************************/
/* EnvSetHaltRules: Sets the HaltRules flag. */
/*********************************************/
globle void EnvSetHaltRules(
  void *theEnv,
  EXEC_STATUS,
  intBool value)
  { 
   EngineData(theEnv,execStatus)->HaltRules = value; 
  }

/****************************************************/
/* EnvGetHaltRules: Returns the HaltExecution flag. */
/****************************************************/
globle intBool EnvGetHaltRules(
  void *theEnv,
  EXEC_STATUS)
  {
   return(EngineData(theEnv,execStatus)->HaltRules);
  }

#endif /* DEFRULE_CONSTRUCT */

